#include "stdafx.h"
#include "CppUnitTest.h"

#include <vex/iterable/stl_interop.h>
#include <vex.tests/mstest/tests.range/tests.range.h>

using namespace Microsoft::VisualStudio::CppUnitTestFramework;

namespace tests {	namespace range_stl_array {

    using namespace boost::assign;

    namespace vi = vex::iterable;

    TEST_CLASS(range_stl_array)
    {
        BEGIN_TEST_CLASS_ATTRIBUTE()
            TEST_CLASS_ATTRIBUTE(L"Module", L"tests.range.stl.array")
        END_TEST_CLASS_ATTRIBUTE();

        static const std::size_t s_container_size = 4u;
        typedef std::array<int, s_container_size> container_t;
        typedef container_t::iterator iterator_t;
        typedef container_t::const_iterator const_iterator_t;
    public:
        TEST_METHOD(supports_move_and_swap)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();

            container_t t_c1 = {
                1, 2, 3, 4
            };
            container_t t_c2 = {
                6, 7, 8, 9
            };

            auto t_r1 = vi::make_range(t_c1);
            auto t_r2 = vi::make_range(t_c2);

            random_access_range::has_expected_type_and_size<vi::stl_interop::range_from_iterator<iterator_t>>(
                t_r1, sizeof(vi::stl_interop::range_from_iterator<iterator_t>));
            random_access_range::has_expected_type_and_size<vi::stl_interop::range_from_iterator<iterator_t>>(
                t_r2, sizeof(vi::stl_interop::range_from_iterator<iterator_t>));
            random_access_range::supports_and_executes_swap_correctly(
                t_c1, t_c2, t_r1, t_r2);
        }

        TEST_METHOD(supports_non_const_to_const_conversion_but_not_vice_versa)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();

            typedef decltype(vi::make_range(
                std::declval<iterator_t>(), std::declval<iterator_t>())) range_t;
            typedef decltype(vi::make_range(
                std::declval<const_iterator_t>(), std::declval<const_iterator_t>())) const_range_t;
            
            Assert::IsTrue(std::is_convertible<range_t, const_range_t>::value);
            Assert::IsFalse(std::is_convertible<const_range_t, range_t>::value);
        }

        TEST_METHOD(each_subsequent_move_front_or_move_back_decreases_size_by_one)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            container_t c = {
                1, 2, 3, 4
            };

            random_access_range::size_always_has_expected_value(
                vi::make_range(c), c.size());

            random_access_range::each_subsequent_move_front_decreases_size_by_one(
                vi::make_range(c));

            random_access_range::each_subsequent_move_back_decreases_size_by_one(
                vi::make_range(c));
        }

        TEST_METHOD(move_front_iteration_succeeds_expected_number_of_times)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            container_t c = {
                1, 2, 3, 4
            };

            std::array<int, 0> c_empty;

            random_access_range::move_front_iteration_succeeds_expected_number_of_times(
                vi::make_range(c_empty), c_empty.size());

            random_access_range::move_front_iteration_succeeds_expected_number_of_times(
                vi::make_range(c), c.size());

            random_access_range::move_front_iteration_succeeds_expected_number_of_times(
                vi::make_range(c.begin(), c.end()), c.size());

            random_access_range::move_front_iteration_succeeds_expected_number_of_times(
                vi::make_range(c.cbegin(), c.cend()), c.size());
        }

        TEST_METHOD(move_front_succeeds_n_times_on_range_with_n_elements)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            container_t c = {
                1, 2, 3, 4
            };
            random_access_range::move_front_succeeds_n_times_on_range_with_n_elements(
                vi::make_range(c), c.cbegin(), c.cend());
        }

        TEST_METHOD(after_calling_move_front_n_times_front_points_to_nth_element)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            container_t c = {
                1, 2, 3, 4
            };
            for (auto t_size = c.size(); t_size > 0; --t_size) {
                random_access_range::after_calling_move_front_n_times_front_points_to_nth_element(
                    vi::make_range(c), t_size, c.cbegin(), c.cend());
            }
        }

        TEST_METHOD(move_back_succeeds_n_times_on_range_with_n_elements)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            container_t c = {
                1, 2, 3, 4
            };
            random_access_range::move_back_succeeds_n_times_on_range_with_n_elements(
                vi::make_range(c), c.crbegin(), c.crend());
        }

        TEST_METHOD(after_calling_move_back_n_times_back_points_to_nth_element_from_back)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            container_t c = {
                1, 2, 3, 4
            };
            for (auto t_size = c.size(); t_size > 0; --t_size) {
                random_access_range::after_calling_move_back_n_times_back_points_to_nth_element_from_back(
                    vi::make_range(c), t_size, c.crbegin(), c.crend());
            }
        }

        TEST_METHOD(either_move_front_or_move_back_are_possible_on_range_with_one_element)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            std::array<int, 1> c = {1};
            random_access_range::either_move_front_or_move_back_are_possible_on_range_with_one_element(
                vi::make_range(c));
        }

        TEST_METHOD(front_and_back_are_equal_on_range_with_one_element)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            std::array<int, 1> c = {1};
            random_access_range::front_and_back_are_equal_on_range_with_one_element(
                vi::make_range(c), c.front());
        }

        TEST_METHOD(at_and_size_are_available_on_not_started_range)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            std::array<int, 4> c = {
                1, 2, 3, 4
            };

            std::array<int, 1> c_one = {
                1
            };

            std::array<int, 0> c_empty;

            {
                std::vector<std::size_t> t_indices;
                t_indices += 0u, 1u, 2u, 3u;
                random_access_range::at_and_size_are_available_on_not_started_range(
                    vi::make_range(c), t_indices.begin(), t_indices.end(), c.begin());        
            }

            {
                std::vector<std::size_t> t_indices;
                t_indices += 0u;
                random_access_range::at_and_size_are_available_on_not_started_range(
                    vi::make_range(c_one), t_indices.begin(), t_indices.end(), c_one.begin());
            }

            {
                std::vector<std::size_t> t_indices;
                random_access_range::at_and_size_are_available_on_not_started_range(
                    vi::make_range(c_empty), t_indices.begin(), t_indices.end(), c_empty.begin());
            }
        }

        TEST_METHOD(front_always_equals_at_of_zero_in_non_empty_range)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            std::array<int, 4> c = {
                1, 2, 3, 4
            };

            std::array<int, 1> c_one = {
                1
            };

            std::array<int, 0> c_empty;

            random_access_range::front_always_equals_at_of_zero_in_non_empty_range(
                vi::make_range(c));

            random_access_range::front_always_equals_at_of_zero_in_non_empty_range(
                vi::make_range(c_one));

            random_access_range::front_always_equals_at_of_zero_in_non_empty_range(
                vi::make_range(c_empty));
        }

        TEST_METHOD(back_always_equals_at_of_size_minus_one_in_non_empty_range)
        {
            VEX_TESTS_SCOPED_MEM_CHECK();
            std::array<int, 4> c = {
                1, 2, 3, 4
            };

            std::array<int, 1> c_one = {
                1
            };

            std::array<int, 0> c_empty;

            random_access_range::back_always_equals_at_of_size_minus_one_in_non_empty_range(
                vi::make_range(c));

            random_access_range::back_always_equals_at_of_size_minus_one_in_non_empty_range(
                vi::make_range(c_one));

            random_access_range::back_always_equals_at_of_size_minus_one_in_non_empty_range(
                vi::make_range(c_empty));
        }
    };
}}